# 授权结果
# {
#     "authExtKey": "450704994719041987",
#     "empExtKey": "cs998",
#     "empType": 11,
#     "pointExtKey": "2001-0000000644",
#     "dataSysCode": 2001,
#     "downloadTime": "2023-04-10 14:16:18",
#     "downloadState": "2099-12-31 23:59:59",
#     "remarks": null,
#     "isDeleted": false,
#     "deleteTime": null,
#     "modifyTime": "2023-04-10 14:16:18",
# }

from plugin import Mylogger, MSsqlClient, Config, MyTime
from module.huada.entity.doorsforbid_entity import AuthEntity
from base import MY_STORED_CACHE, UPDATE_MY_STORED_CACHE
from config import Config
import json

# 默认无效时间，none无法被对方解析，所以用一个默认时间
default_time = '2000-01-01 00:00:00'

sql = """
select
	top(10000)
    authExtKey,
    empExtKey,
    empType,
    pointExtKey,
    dataSysCode,
    create_time as downloadTime,
    '' as downloadState,
    '' as remarks,
    isDeleted,
    deleteTime,
    modifyTime,
    isdeal
from
    auth
where id > {}
order by id
"""

# authstate字段对应的授权状态
# authstate是在isdeal字段基础上，根据授权结果进行进行判断的真实授权状态
auth_state = {
    "1": "授权成功",
    "2": "删除授权成功",
    # "3": "授权失败,部分凭证授权失败,请检查设备在线情况",
    # "4": "授权失败,部分人员删除授权失败",
    # "-3": "授权失败,数据异常,请检查用户信息是否正确",
    # "-1": "授权失败,重试次数1",
    # "-2": "授权失败,重试次数2",
    # "-99": "授权信息异常"
    "-1": "人员不存在，请确认人员是否存在",
    "-2": "凭证异常，请检查凭证",
    "-3": "门点异常，请检查门点是否存在",
    "-4": "授权，请确认设备是否在线",
}


class AuthResultEntity():
    url = "tunion/put/download"
    table_name = "auth"
    # get_time = "2020-01-01 00:00:00"
    # order_by = "sysModifyTime"
    auth_datas: list = []
    wirte_back_datas = {}
    create_table = True
    update_cache_time = default_time

    def __init__(self, **kwargs) -> None:
        """
        :param authExtKey: 授权扩展主键
        :param empExtKey: 员工扩展主键
        :param empType: 员工类型
        :param pointExtKey: 门禁点扩展主键
        :param dataSysCode: 数据来源系统编码
        :param downloadTime: 下载时间
        :param downloadState: 下载状态,1-成功 2-失败
        :param remarks: 备注
        :param isDeleted: 是否删除
        :param deleteTime: 删除时间
        :param modifyTime: 修改时间
        :param isdeal: 处理状态
        :param sendstate: 发送状态 0-未发送 1-已发送 -n-发送失败重试次数
        """
        self.authExtKey = kwargs.get("authExtKey", "")
        self.empExtKey = kwargs.get("empExtKey", "")
        self.empType = kwargs.get("empType", 0)
        self.pointExtKey = kwargs.get("pointExtKey", "")
        self.dataSysCode = kwargs.get("dataSysCode", 0)
        self.downloadTime = kwargs.get("downloadTime", default_time)
        self.isdeal = kwargs.get("isdeal", 0)
        self.authstate = kwargs.get("authresult", self.isdeal)
        if int(self.authstate) in (1, 2):
            self.downloadState = 1
        else:
            self.downloadState = 2
        self.remarks = auth_state[str(self.authstate)]
        self.isDeleted = kwargs.get("isDeleted", False)
        self.deleteTime = kwargs.get("deleteTime", default_time)
        self.deleteTime = default_time if self.deleteTime == "None" else self.deleteTime
        self.modifyTime = kwargs.get("modifyTime", default_time)
        self.sendstate = kwargs.get("sendstate", 0)
        self.id = kwargs.get("id", 0)

    @classmethod
    def circ_check_data(cls, url, content):
        """
        循环检查未发送数据，并更新授权状态
        """
        client = MSsqlClient(charset='cp936', **
                             Config.module_config.huada.record_link)
        if cls.create_table:
            client.execute('''
                           if not exists(select * from syscolumns where id=object_id('auth') and name='sendstate')
                           begin
                               alter table auth add sendstate int default 0
                               
                           end
                           update auth set sendstate = 0 where sendstate is null
                           if not exists(select * from syscolumns where id=object_id('auth') and name='authresult')
                           begin
                               alter table auth add authresult int default 1
                               alter table auth add reason varchar(128) default ''
                           end
                           ''')
            client.execute('''
                           update auth set authresult = 1 where authresult is null
                           update auth set reason = 0 where reason is null
                           ''')
            cls.create_table = False

        # 按id顺序获取数据，这里记录上次获取的id，下次从这个id开始获取
        if not MY_STORED_CACHE.__contains__(cls.table_name + "_crc_check_index_id"):
            UPDATE_MY_STORED_CACHE(cls.table_name + "_crc_check_index_id", 0)
        crc_check_index_id = MY_STORED_CACHE[cls.table_name +
                                             "_crc_check_index_id"]

        results = client.select_to_dict(
            f"select top(10000) * from {cls.table_name} where id > {crc_check_index_id} and sendstate between -2 and 0 and (isdeal <=-3 or isdeal > 0) order by id")
        # 如果数据少于10000条，说明已经检查完毕，将检查的id重置为0
        if len(results) < 10000:
            UPDATE_MY_STORED_CACHE(
                cls.table_name + "_crc_check_index_id", 0)
        else:
            UPDATE_MY_STORED_CACHE(
                cls.table_name + "_crc_check_index_id", results[-1]['id'])

        sort_data(results)

        # 根据回写结果更新发送状态
        for key, value in cls.wirte_back_datas.items():
            # 每1000条数据回写
            for id in range(0, len(value), 1000):
                _ids = value[id:id+1000]
                ids = [str(id) for id in _ids]
                client.execute(
                    f"update {cls.table_name} set sendstate = {key} where id in ({','.join(ids)})")
        cls.wirte_back_datas.clear()
        # 发送数据
        import requests
        # 每1000条数据发送一次
        send_datas = []
        for data in cls.auth_datas:
            # 保留none值，默认会变成null，对方接口只能解析null
            _dict = data.__dict__
            _dict['deleteTime'] = None
            # send_datas.append(data.__dict__)
            send_datas.append(_dict)
            if len(send_datas) == 100 or data == cls.auth_datas[-1]:
                content['data'] = send_datas
                r = requests.post(url+cls.url, json=content, logger = None)
                if r.status_code == 200:
                    res = r.json()
                    if res.__contains__("code") and res["code"] == 200:
                        Mylogger.info("授权数据发送成功100条")
                    else:
                        Mylogger.error("授权数据发送失败100条")
                        Mylogger.error(res)
                else:
                    Mylogger.error("授权数据发送失败100条")
                    Mylogger.error(r.text)
                send_datas.clear()
        cls.auth_datas.clear()
        if len(results) > 0:
            Mylogger.info(
                f"授权数据发送完毕，共发送{len(results)}条数据, index {results[0]['id']}->{results[-1]['id']}")
        else:
            Mylogger.info("授权数据发送完毕，无新增数据")


def sort_data(results: list[AuthEntity.__dict__]):
    """
    整理数据，获取授权数据的真实授权状态
    :param results: 数据
    :return: 排序后的数据
    """
    # 每天更新一次人卡缓存
    # if MyTime.timestamp() - int(MyTime.timestampfrom(AuthResultEntity.update_cache_time)) > 24 * 60 * 60:
    #     get_usercard_cache()
    #     AuthResultEntity.update_cache_time = MyTime.timestr()


    auth_obj = None
    for result in results:
        # 处理成功的数据，检查真实授权状态
        if int(result['isdeal']) in (1, 2):
            result['authresult'], result['downloadTime'] = check_auth(result, result['isdeal'])

        auth_obj = AuthResultEntity(**result)
        auth_obj.sendstate = 1

        # # 如果授权状态为1，2 表示成功，发送状态设为1
        # # 如果授权状态为-3，表示失败，发送状态设为1,不再发送
        # if int(auth_obj.isdeal) in (1, 2, -3, -99):
        #     auth_obj.sendstate = 1
        # else:
        #     auth_obj.sendstate -= 1  # 部分授权成功，发送状态减1

        if not AuthResultEntity.wirte_back_datas.__contains__(auth_obj.sendstate):
            AuthResultEntity.wirte_back_datas[auth_obj.sendstate] = []
        AuthResultEntity.wirte_back_datas[auth_obj.sendstate].append(
            auth_obj.id)
        AuthResultEntity.auth_datas.append(AuthResultEntity(**result))


check_auth_sql = """
select
    a.state,a.cardid,b.addr,ISNULL(updatetime,downtime) as time
from acscon_downCardToDoorRef a
inner join acscon_realnode b on a.realnodeid = b.id
where a.cardid in ('{}') and b.addr = '{}'
"""


def check_auth(authdata: AuthEntity.__dict__, auth_type) -> int:
    """
    检查授权结果
    :param authdata: 授权数据
    :param auth_type: 授权类型 1-授权 2-删除授权
    :return: 授权结果 1-授权成功 -2 凭证异常 -4 授权失败，门点可能离线
    """
    last_auth_time = float(MyTime.timestampfrom(default_time))
    # usercards: usercard = usercard_cache.get(
    #     authdata['empExtKey'], [])  # 获取人员卡
    usercards = get_usercard(authdata['empExtKey'])
    if len(usercards) == 0:
        # 如果人员没有卡，则直接返回
        return -2, MyTime.timestrfrom(last_auth_time)
    else:
        cardstr = "','".join([str(card.cardid) for card in usercards])
        dooraddr = analyse_point_ext_key(authdata['pointExtKey'])
        client = MSsqlClient(charset='cp936', **
                             Config.module_config.huada.client_link)
        result = client.select_to_dict(
            check_auth_sql.format(cardstr, dooraddr))
        if (int(auth_type) == 1):  # 正向授权
            if len(result) == 0:
                return -4, MyTime.timestrfrom(last_auth_time)  # 凭证信息正常，但是没有查到授权，返回控制器可能离线
            elif len(result) == len(usercards):
                for res in result:
                    # _timestamp = MyTime.timestampfrom(res['time'])
                    _timestamp = res['time'].timestamp()
                    if last_auth_time < _timestamp:
                        last_auth_time = _timestamp
                    if res['state'] != 0:
                        # 如果有一张卡没有授权成功，则返回失败
                        return -4, MyTime.timestrfrom(last_auth_time)
                # 如果授权成功，则返回
                return 1, MyTime.timestrfrom(last_auth_time)
            else:
                # 如果授权失败，则返回
                return -4, MyTime.timestrfrom(last_auth_time)
        else:
            if len(result) == 0:
                # 如果删除成功，则返回
                return 2, MyTime.timestrfrom(last_auth_time)
            else:
                for res in result:
                    if res['state'] == 0:
                        # 如果有一张卡没有删除成功，则返回失败
                        return -4, MyTime.timestrfrom(last_auth_time)
                # 全都是异常授权，则返回成功
                return 2, MyTime.timestrfrom(last_auth_time)


class usercard:
    def __init__(self, **kwargs) -> None:
        self.usercode = kwargs.get("usercode", "")  # 人员编号
        self.uid = kwargs.get("uid", "")  # 人员ID
        self.cardno = kwargs.get("cardno", "")  # 卡号
        self.cardid = kwargs.get("cardid", "")  # 卡ID
        self.status = kwargs.get("status", 1)  # 卡状态 1-正常 其他-异常


usercard_cache = {}


def get_usercard_cache():
    """
    获取人员卡缓存
    :return:
    """
    client = MSsqlClient(charset='cp936', **
                         Config.module_config.huada.client_link)
    results = client.select_to_dict(
        """
        select 
            usercode,
            uid,
            cardid,
            cardno,
            c.status 
        from acscon_usercard a
        inner join acscon_user b on a.uid = b.id
        inner join acscon_card c on a.cardid = c.id
        """)
    # 将数据按照usercode分成字典
    if results and len(results) > 0:
        usercard_cache.clear()
    for result in results:
        usercode = result['usercode']
        if not usercard_cache.__contains__(usercode):
            usercard_cache[usercode] = []
        usercard_cache[usercode].append(usercard(**result))



def get_usercard(usercode):
    """
    获取人员卡缓存
    :return:
    """
    client = MSsqlClient(charset='cp936', **
                         Config.module_config.huada.client_link)
    results = client.select_to_dict(
        f"""
        select 
            usercode,
            uid,
            cardid,
            cardno,
            c.status 
        from acscon_usercard a
        inner join acscon_user b on a.uid = b.id
        inner join acscon_card c on a.cardid = c.id
        where 
            c.status = 1 and
            b.usercode = '{usercode}'
        """)
    usercards = []

    if results and len(results) > 0:
        usercards.clear()
    for result in results:
        usercards.append(usercard(**result))
    return usercards


def get_point_ext_key(addr):
    """
    获取门点对接键
    :param addr: 门点地址 100-1#2-3
    :return: 门点对接键 2001-100001002003
    """
    addr = addr.replace('#', '-')
    addrs = addr.split('-')
    # 每一位前面补零，到3位，然后拼接
    addrs = [str(i).zfill(3) for i in addrs]
    return str(Config.module_config.huada.system_code) + '-' + ''.join(addrs)


def analyse_point_ext_key(point_ext_key):
    """
    解析门点对接键
    :param point_ext_key: 门点对接键，eg: 2001-100001002003
    :return: 地址 100-1#2-3
    """
    point_ext_key = point_ext_key.replace(
        str(Config.module_config.huada.system_code)+'-', '')
    addrs = []
    for i in range(0, len(point_ext_key), 3):
        addrs.append(str(int(point_ext_key[i:i + 3])))
    return f"{addrs[0]}-{addrs[1]}#{addrs[2]}-{addrs[3]}"


if __name__ == '__main__':
    print(get_point_ext_key('100-1#2-10'))
    print(analyse_point_ext_key('2001-100001002030'))
